﻿using System;
using System.Collections;
using System.Text;

namespace _29._1_面向对象_泛型
{
    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person<string ,int>("小A","aaa",22);
            p1.Eat();
            p1.Drink();


            //var obj = new object();
            //var sa = new Sample<Base>((Base)obj);

            //ArrayList array = new ArrayList();
            //array.Add(111);
            //array.Add("222");
            //array.Add(new Base());


            // 测试获取任意接口 ，as 复习
            //var bs = new Base();
            //var sp = new Sample<Base>(bs);
            //IEatable et = sp.ReturnInterface();
            //Console.WriteLine(et.GetType());
            //IEatable2 et2 = sp.ReturnInterface2();
            //Console.WriteLine("et2"+et2);

            var ps1 = new Sample<int>(0);
            Console.WriteLine($"数值与 null 判断 = {ps1.Comparenull()}"); //值类型永远返回 false ，无判定是否为null

            var ps2 = new Sample<Base>(new Base());
            Console.WriteLine($"对象与 null 判断 = {ps2.Comparenull()}");

            ps2 = new Sample<Base>(null);
            Console.WriteLine($"对象与 null 判断 = {ps2.Comparenull()}"); //可以判断对象实例是否为null

            // int? : 可空类型，见 43 行，可以赋 null 值
            // string ?
            var p3 = new Sample<int?>(555);
            Console.WriteLine($"数值与 null 判断 = {p3.Comparenull()}"); 
            p3 = new Sample<int?>(null);
            Console.WriteLine($"数值与 null 判断 = {p3.Comparenull()}");


        }
    }

    /// <summary>
    /// 人类
    /// </summary>
    /// <typeparam name="T">素材 A</typeparam>
    /// <typeparam name="V">素材 B</typeparam>
    // 多个泛型进行约束，使用多个 where 关键字，且无需使用“ ，”
    public class Base:IEatable
    { }
    //public class Person<T, V> where T : Base where V : struct
    public class Person<T, V> where T : class where V : struct
    {
        private T arg_t;
        private V arg_v;
        private string name;

        public Person(string name, T arg_t, V arg_v)
        {
            this.arg_t = arg_t;
            this.arg_v = arg_v;
            this.name = name;
        }

        public void Eat()
        {
            Console.WriteLine($"{name} 在吃 { arg_t} - {arg_v.GetType()} 待会儿喝它");


            // 通过下面的示例说明，在 泛型 T where:class 时，避免使用 “==” “!=”,因为有可能与预期的结构不符。
            // 因为这些运算符将仅测试引用身份,而不测试值相等性.
            string a = "你好";
            string b = "你好";
            string c = new StringBuilder("你好").ToString();
            Console.WriteLine(a==b); // 测试值相等
            Console.WriteLine(Compare<string>(a,b)); //引用地址也相等

            Console.WriteLine(a == c); //测试值相等
            Console.WriteLine(Compare<string>(a, c)); //引用地址不相等
        }


        public bool Compare<U>(U lv,U rv)  where U:class
        {
            return lv == rv;
        }

        public void Drink()
        {
            Console.WriteLine($"{name} 在喝 {arg_v}");
        }
    }

    //sample :取样，样本，示例，例子
    // 未做类型约束的，或未指定类型 称为未绑定的类型参数
    //没有约束的类型参数（如公共类 SampleClass<T>{}中的 T）称为未绑定的类型参数。
    //未绑定的类型参数具有以下规则：
    //1.  不能使用 !=和 == 运算符，因为无法保证具体类型参数能支持这些运算符。
    //2. 可以在它们与 System.Object之间来回转换(类型转换)，或将它们显式转换为任何接口类型。
    //因为没有指定是class 还是 struct ，所以用最大兼容性object
    // 3. 可以将它们与 [null] 进行比较。 将未绑定的参数与 null进行比较时，如果类型参数为值类型，则该比较将始终返回 false。
    public class Sample<T>  
    {
        private T value;
        public Sample(T value) 
        {
            this.value = value;
        }
        // lv = left value ,左边的值
        // rv = right value，右边的值
        //public bool Compare(T lv,T rv)
        //{
        //    //return lv == rv;
        //    return lv != rv;
        //}

        public object RetureT() 
        {
            return value;
        }

        public IEatable ReturnInterface() 
        {
            return value as IEatable;
        }

        public IEatable2 ReturnInterface2()
        {
            return value as IEatable2;
        }

        public bool Comparenull() 
        {
            return value == null;
        }
    }
    public interface IEatable { }
    public interface IEatable2 { }

}
